package oracle.olapi.examples;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import oracle.olapi.data.cursor.CompoundCursor;
import oracle.olapi.data.cursor.Cursor;
import oracle.olapi.data.cursor.IncorrectDataTypeException;
import oracle.olapi.data.cursor.NotSpecifiedException;
import oracle.olapi.data.cursor.ValueCursor;

/**
 * Displays the values of an OLAP API Cursor.
 *
 * @author Oracle Corporation
 */
public class CursorPrintWriter extends PrintWriter {
    
    /**
     * Creates a new CursorPrintWriter object and sends the display to
     * System.out.
     */
    static CursorPrintWriter stdOut = null;
    
    static public CursorPrintWriter getStandardOutput() {
        if ( null == stdOut )
            stdOut = new UncloseablePrintWriter(System.out);
        return stdOut;
    }
    
    /**
     * Required constructor.
     */
    public CursorPrintWriter(PrintWriter dataWriter) {
        super(dataWriter, true);
    }
    
    /**
     * Iterates through the Cursor structure and prints the values of the
     * Cursors.
     * Puts a open parentheses character, "(", before the first value in a
     * CompoundCursor and a close parentheses character, ")", after the last
     * value in the CompoundCursor.
     * Puts a comma, ",", between the values of nested ValueCursor objects.
     * Displays "NA" if the value for a ValueCursor in a tuple has no value.
     * (A tuple is the set of the values of the outputs of a CompoundCursor
     * for a position of the ValueCursor for that CompoundCursor.
     *
     * @param cursor The Cursor whose values you want to display.
     *
     * @param displayLocVal A boolean that specifies whether to display
     *                      unique or local dimension element values.
     */
    private void _printTuple(Cursor cursor, boolean displayLocVal) {
        if(cursor instanceof CompoundCursor) {
            CompoundCursor compoundCursor = (CompoundCursor)cursor;
            print("(");
            Iterator iterOutputs = compoundCursor.getOutputs().iterator();
            Cursor output = (Cursor)iterOutputs.next();
            _printTuple(output, displayLocVal);
            while(iterOutputs.hasNext()) {
                print(",");
                _printTuple((Cursor)iterOutputs.next(), displayLocVal);
            }
            print(",");
            _printTuple(compoundCursor.getValueCursor(), displayLocVal);
            print(")");
            _printParentInfo(cursor);
        } else if(cursor instanceof ValueCursor) {
            ValueCursor valueCursor = (ValueCursor) cursor;
            if (valueCursor.hasCurrentValue()) {
                if (!displayLocVal)
                    print(valueCursor.getCurrentValue());
                else {
                    int index = 0;
                    Object value = valueCursor.getCurrentValue();
                    if (value instanceof String) {
                        String uVal = valueCursor.getCurrentString();
                        if (uVal.indexOf("::") > 0) {
                            index = uVal.lastIndexOf("::");
                            String lVal = uVal.substring((uVal.lastIndexOf("::") + 2),
                                    uVal.length());
                            print(lVal);
                        } else
                            print(value);
                    } else
                        print(value);
                }
            } else
                print("NA");
            _printParentInfo(cursor);
        }
    }
    
    /**
     * Displays an integer followed by a colon to indicate the current position
     * of the root CompoundCursor or the ValueCursor.
     * Sends the Cursor to the private method _printTuple, which iterates through
     * the Cursor structure and prints the values of the Cursor objects.
     *
     * @param cursor The Cursor whose values you want to display.
     *
     * @param displayLocVal A boolean that specifies whether to display
     *                      unique or local dimension element values.
     */
    public void printCursor(Cursor rootCursor, boolean displayLocVal) {
        int i = 1;
        do
        {
            print(i++ + ": ");
            _printTuple(rootCursor, displayLocVal);
            println();
            flush();
        } while(rootCursor.next());
    }
    
    private void _printParentInfo(Cursor cursor) {
        try {
            long parentStart = cursor.getParentStart();
            long parentEnd = cursor.getParentEnd();
            print("{" + parentStart + "," + parentEnd + "}");
        } catch(NotSpecifiedException e) { } // ignore
    }
    
    public void printTopBottomResult(ValueCursor valueCursor) {
        int i = 1;
        do
        {
            try {
                println(i + ". " + Math.round(valueCursor.getCurrentDouble()));
            } catch(IncorrectDataTypeException e) {
                println(i + ". " + valueCursor.getCurrentValue());
            }
            i++;
        } while(valueCursor.next());
        //cpw.displayTopBottomResult(cursor);
    }
    
    // This method expects a CompoundCursor.
    public void printAsCrosstab(CompoundCursor rootCursor) {
        List outputs = rootCursor.getOutputs();
        int nOutputs = outputs.size();
        
        // Set the initial positions of all outputs
        Iterator outputIter = outputs.iterator();
        while (outputIter.hasNext())
            ((Cursor) outputIter.next()).setPosition(1);
        
        // The last output is fastest-varying; it represents columns.
        // The next to last output represents rows.
        // All other outputs are on the page.
        Cursor colCursor = (Cursor) outputs.get(nOutputs - 1);
        Cursor rowCursor = (Cursor) outputs.get(nOutputs - 2);
        ArrayList pageCursors = new ArrayList();
        for (int i = 0 ; i < nOutputs - 2 ; i++) {
            pageCursors.add(outputs.get(i));
        }
        
        // Get the base ValueCursor, which has the data values
        ValueCursor dataCursor = rootCursor.getValueCursor();
        
        // Print the pages of the crosstab
        printPages(pageCursors, 0, rowCursor, colCursor, dataCursor);
    }
    
    // Prints the pages of a crosstab
    private void printPages(List pageCursors, int pageIndex, Cursor rowCursor,
            Cursor colCursor, ValueCursor dataCursor) {
        // Get a Cursor for this page
        Cursor pageCursor = (Cursor) pageCursors.get(pageIndex);
        
        // Loop over the values of this page dimension
        do {
            // If this is the fastest-varying page dimension, print a page
            if (pageIndex == pageCursors.size() - 1) {
                // Print the values of the page dimensions
                printPageHeadings(pageCursors);
                
                // Print the column headings
                printColumnHeadings(colCursor);
                
                // Print the rows
                printRows(rowCursor, colCursor, dataCursor);
                
                // Print a couple of blank lines to delimit pages
                println();
                println();
            }
            
            // If this is not the fastest-varying page, recurse to the
            // next fastest varying dimension.
            else {
                printPages(pageCursors, pageIndex + 1, rowCursor, colCursor,
                        dataCursor);
            }
        } while (pageCursor.next());
        
        // Reset this page dimension Cursor to its first element.
        pageCursor.setPosition(1);
    }
    
    // Prints the values of the page dimensions on each page
    private void printPageHeadings(List pageCursors) {
        // Print the values of the page dimensions
        Iterator pageIter = pageCursors.iterator();
        while (pageIter.hasNext()) {
            //dgm added ****
            Cursor cursor = (Cursor) pageIter.next();
            if (cursor instanceof CompoundCursor) {
                CompoundCursor cc = (CompoundCursor) cursor;
                List outputs = cc.getOutputs();
                ValueCursor output = (ValueCursor) outputs.get(0);
                String value = output.getCurrentString();
                println(value);
            } else {
                ValueCursor vc = (ValueCursor) cursor;
                String value = vc.getCurrentString();
                println(getLocalValue(value));
            }
        }
        println();
    }
    
    // Prints the column headings on each page
    private void printColumnHeadings(Cursor colCursor) {
        do
        {
            print("\t");
            if (colCursor instanceof CompoundCursor) {
                CompoundCursor cc = (CompoundCursor) colCursor;
                List outputs = cc.getOutputs();
                ValueCursor output = (ValueCursor) outputs.get(0);
                String value = output.getCurrentString();
                print(value);
            } else {
                ValueCursor vc = (ValueCursor) colCursor;
                String value = vc.getCurrentString();
                print(getLocalValue(value));
            }
        } while (colCursor.next());
        println();
        colCursor.setPosition(1);
    }
    
    // Prints the rows of each page
    private void printRows(Cursor rowCursor, Cursor colCursor,
            ValueCursor dataCursor) {
        // Loop over rows
        do {
            // Print row dimension value
            if (rowCursor instanceof CompoundCursor) {
                CompoundCursor cc = (CompoundCursor) rowCursor;
                List outputs = cc.getOutputs();
                ValueCursor output = (ValueCursor) outputs.get(0);
                String value = output.getCurrentString();
                print(value);
            } else {
                ValueCursor vc = (ValueCursor) rowCursor;
                String value = vc.getCurrentString();
                print(getLocalValue(value));
            }
            print("\t");
            // Loop over columns
            do {
                // Print data value
                try {
                    print(Math.round(dataCursor.getCurrentDouble()));
                } catch(IncorrectDataTypeException e) {
                    print(dataCursor.getCurrentValue());
                }
                print("\t");
            } while (colCursor.next());
            println();
            
            // Reset the column Cursor to its first element
            colCursor.setPosition(1);
        } while (rowCursor.next());
        
        // Reset the row Cursor to its first element
        rowCursor.setPosition(1);
    }
    
    private String getLocalValue(String uniqueValue) {
        int index = 0;
        if (uniqueValue.indexOf("::") > 0) {
            index = uniqueValue.lastIndexOf("::");
            String localValue = uniqueValue.substring((uniqueValue.lastIndexOf("::")
            + 2),
                    uniqueValue.length());
            return(localValue);
        } else
            return uniqueValue;
    }
    
    // Model an uncloseable PrintWriter for use with System.out
    static private class UncloseablePrintWriter extends CursorPrintWriter {
        
        public UncloseablePrintWriter(OutputStream out) {
            super(
               new PrintWriter(
                    new BufferedWriter(new OutputStreamWriter(out)),
                    true)
                );
        }
        public void close() { } // ignore
        
    }
}

